﻿#include "FITKOCCSplitter.h"
#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Edge.hxx>
#include <Precision.hxx>
#include <BOPAlgo_Splitter.hxx>
#include <TopExp_Explorer.hxx>
#include <BRep_Builder.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <gp_Pln.hxx>

#include "FITK_Interface/FITKInterfaceGeometry/FITKGeoEnum.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKVirtualTopoManager.h"
#include "FITKOCCVirtualTopoCreator.h"

#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKGeoCommandList.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKGeoInterfaceFactory.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKAbsGeoModelSurface.h"

namespace OCC {

    // 构造函数
    FITKOCCCurveSplitter::FITKOCCCurveSplitter() :OCCShapeAgent(this)
    {
        _shapeAgent = _occShapeAgent;
    }

    // 更新数据
    bool FITKOCCCurveSplitter::update()
    {
        if (m_SourceShape.isNull() || m_ToolShape.isNull()) return false;

        auto geoCmdList = FITKGLODATA->getGeometryData<Interface::FITKGeoCommandList>();

        // 分割算法
        BOPAlgo_Splitter splitter;

        // 设置被分割的对象
        auto sourceCmd = geoCmdList->getDataByID(m_SourceShape.CmdId);
        if (sourceCmd == nullptr) return false;
        auto sourceShape = sourceCmd->getShapeT<FITKOCCTopoShape>(Interface::FITKGeoEnum::VTopoShapeType::VSEdge, m_SourceShape.VirtualTopoId);
        if (sourceShape == nullptr) return false;
        TopoDS_Shape sourceTopoShape = sourceShape->getTopoShape();
        if (sourceTopoShape.IsNull()) return false;
        splitter.AddArgument(sourceTopoShape);

        // 设置分割的工具对象
        bool isDatumTool = m_ToolShape.isDatumObj();
        if (isDatumTool) {

            auto datum = geoCmdList->getDatumManager()->getDataByID(m_ToolShape.datumId());
            if (datum == nullptr) return false;
            int type = static_cast<int>(datum->getDatumType());
            // 参考点
            TopoDS_Shape toolShape;
            if (type < static_cast<int>(Interface::FITKGeoEnum::FITKDatumType::FDTLine)) {
                auto refPoint = dynamic_cast<Interface::FITKAbsGeoDatumPoint*>(datum);
                if (refPoint == nullptr) return false;
                double xyz[3]{};
                refPoint->getPosition(xyz);
                BRepBuilderAPI_MakeVertex vertex(gp_Pnt(xyz[0], xyz[1], xyz[2]));
                toolShape = vertex.Shape();
            }
            else if (type < static_cast<int>(Interface::FITKGeoEnum::FITKDatumType::FDTPlane)) {
                auto refLine = dynamic_cast<Interface::FITKAbsGeoDatumLine*>(datum);
                if (refLine == nullptr) return false;
                double start[3]{}, end[3]{};
                refLine->getPosition(start);
                refLine->getPosition2(end);
                BRepBuilderAPI_MakeEdge edge(gp_Pnt(start[0], start[1], start[2]), gp_Pnt(end[0], end[1], end[2]));
                toolShape = edge.Shape();
            }
            else {
                auto refPlane = dynamic_cast<Interface::FITKAbsGeoDatumPlane*>(datum);
                if (refPlane == nullptr) return false;
                double loc[3]{}, normal[3]{}, up[3]{};
                refPlane->getPlane(loc, normal, up);
                gp_Pln plane(
                    gp_Pnt(loc[0], loc[1], loc[2]),
                    gp_Dir(normal[0], normal[1], normal[2])
                );
                BRepBuilderAPI_MakeFace face(plane);
                toolShape = face.Shape();
            }
            if (toolShape.IsNull()) return false;
            splitter.AddTool(toolShape);
        }
        else {
            auto toolCmd = geoCmdList->getDataByID(m_ToolShape.cmdId());
            if (toolCmd == nullptr) return false;
            auto toolShape = toolCmd->getShapeT<FITKOCCTopoShape>(m_ToolShape.virtualTopoId());
            if (toolShape == nullptr) return false;
            TopoDS_Shape toolTopoShape = toolShape->getTopoShape();
            if (toolTopoShape.IsNull()) return false;
            splitter.AddTool(toolTopoShape);
        }

        // 更新分割
        try {
            splitter.Perform();

            BRep_Builder aBuilder;
            TopoDS_Compound aCompound;
            aBuilder.MakeCompound(aCompound);

            TopExp_Explorer exp;
            int index = 0;
            for (exp.Init(splitter.Shape(), TopAbs_EDGE); exp.More(); exp.Next())
            {
                aBuilder.Add(aCompound, exp.Current());
            }
            _occShapeAgent->updateShape(aCompound);
            /*if (toolTopoShape.ShapeType() == TopAbs_ShapeEnum::TopAbs_VERTEX) {
                gp_Pnt pnt = BRep_Tool::Pnt(TopoDS::Vertex(toolTopoShape));
                m_Point = { pnt.X(), pnt.Y(), pnt.Z() };
            }*/
        }
        catch (...)
        {
            printLog(tr("Failed to split!"), 3);
            return false;
        }
        return true;
    }

    // 构造函数
    FITKOCCSurfaceSplitter::FITKOCCSurfaceSplitter() :OCCShapeAgent(this)
    {
        _shapeAgent = _occShapeAgent;
    }

    // 更新数据
    bool FITKOCCSurfaceSplitter::update()
    {
        if (m_SourceShape.isNull() || m_ToolShape.isNull()) return false;

        auto geoCmdList = FITKGLODATA->getGeometryData<Interface::FITKGeoCommandList>();

        // 分割算法
        BOPAlgo_Splitter splitter;

        // 设置被分割的对象
        auto sourceCmd = geoCmdList->getDataByID(m_SourceShape.CmdId);
        if (sourceCmd == nullptr) return false;
        auto sourceShape = sourceCmd->getShapeT<FITKOCCTopoShape>(Interface::FITKGeoEnum::VTopoShapeType::VSFace, m_SourceShape.VirtualTopoId);
        if (sourceShape == nullptr) return false;
        TopoDS_Shape sourceTopoShape = sourceShape->getTopoShape();
        if (sourceTopoShape.IsNull()) return false;
        splitter.AddArgument(sourceTopoShape);

        // 设置分割的工具对象
        bool isDatumTool = m_ToolShape.isDatumObj();
        if (isDatumTool) {

            auto datum = geoCmdList->getDatumManager()->getDataByID(m_ToolShape.datumId());
            if (datum == nullptr) return false;
            int type = static_cast<int>(datum->getDatumType());
            // 参考点
            TopoDS_Shape toolShape;
            if (type < static_cast<int>(Interface::FITKGeoEnum::FITKDatumType::FDTLine)) {
                auto refPoint = dynamic_cast<Interface::FITKAbsGeoDatumPoint*>(datum);
                if (refPoint == nullptr) return false;
                double xyz[3]{};
                refPoint->getPosition(xyz);
                BRepBuilderAPI_MakeVertex vertex(gp_Pnt(xyz[0], xyz[1], xyz[2]));
                toolShape = vertex.Shape();
            }
            else if (type < static_cast<int>(Interface::FITKGeoEnum::FITKDatumType::FDTPlane)) {
                auto refLine = dynamic_cast<Interface::FITKAbsGeoDatumLine*>(datum);
                if (refLine == nullptr) return false;
                double start[3]{}, end[3]{};
                refLine->getPosition(start);
                refLine->getPosition2(end);
                BRepBuilderAPI_MakeEdge edge(gp_Pnt(start[0], start[1], start[2]), gp_Pnt(end[0], end[1], end[2]));
                toolShape = edge.Shape();
            }
            else {
                auto refPlane = dynamic_cast<Interface::FITKAbsGeoDatumPlane*>(datum);
                if (refPlane == nullptr) return false;
                double loc[3]{}, normal[3]{}, up[3]{};
                refPlane->getPlane(loc, normal, up);
                gp_Pln plane(
                    gp_Pnt(loc[0], loc[1], loc[2]),
                    gp_Dir(normal[0], normal[1], normal[2])
                );
                BRepBuilderAPI_MakeFace face(plane);
                toolShape = face.Shape();
            }
            if (toolShape.IsNull()) return false;
            splitter.AddTool(toolShape);
        }
        else {
            auto toolCmd = geoCmdList->getDataByID(m_ToolShape.cmdId());
            if (toolCmd == nullptr) return false;
            auto toolShape = toolCmd->getShapeT<FITKOCCTopoShape>(m_ToolShape.virtualTopoId());
            if (toolShape == nullptr) return false;
            TopoDS_Shape toolTopoShape = toolShape->getTopoShape();
            if (toolTopoShape.IsNull()) return false;
            splitter.AddTool(toolTopoShape);
        }

        // 更新分割
        try {
            splitter.Perform();

            BRep_Builder aBuilder;
            TopoDS_Compound aCompound;
            aBuilder.MakeCompound(aCompound);

            TopExp_Explorer exp;
            int index = 0;
            for (exp.Init(splitter.Shape(), TopAbs_FACE); exp.More(); exp.Next())
            {
                aBuilder.Add(aCompound, exp.Current());
            }
            _occShapeAgent->updateShape(aCompound);
        }
        catch (...)
        {
            printLog(tr("Failed to split!"), 3);
            return false;
        }
        return true;
    }

    // 构造函数
    FITKOCCSolidSplitter::FITKOCCSolidSplitter() :OCCShapeAgent(this)
    {
        _shapeAgent = _occShapeAgent;
    }

    // 更新数据
    bool FITKOCCSolidSplitter::update()
    {
        if (m_SourceShape.isNull() || m_ToolShape.isNull()) return false;

        auto geoCmdList = FITKGLODATA->getGeometryData<Interface::FITKGeoCommandList>();

        // 分割算法
        BOPAlgo_Splitter splitter;

        // 设置被分割的对象
        auto sourceCmd = geoCmdList->getDataByID(m_SourceShape.CmdId);
        if (sourceCmd == nullptr) return false;
        auto sourceShape = sourceCmd->getShapeT<FITKOCCTopoShape>(Interface::FITKGeoEnum::VTopoShapeType::VSSolid, m_SourceShape.VirtualTopoId);
        if (sourceShape == nullptr) return false;
        TopoDS_Shape sourceTopoShape = sourceShape->getTopoShape();
        if (sourceTopoShape.IsNull()) return false;
        splitter.AddArgument(sourceTopoShape);

        // 设置分割的工具对象
        bool isDatumTool = m_ToolShape.isDatumObj();
        if (isDatumTool) {

            auto datum = geoCmdList->getDatumManager()->getDataByID(m_ToolShape.datumId());
            if (datum == nullptr) return false;
            int type = static_cast<int>(datum->getDatumType());
            // 参考点
            TopoDS_Shape toolShape;
            if (type < static_cast<int>(Interface::FITKGeoEnum::FITKDatumType::FDTLine)) {
                auto refPoint = dynamic_cast<Interface::FITKAbsGeoDatumPoint*>(datum);
                if (refPoint == nullptr) return false;
                double xyz[3]{};
                refPoint->getPosition(xyz);
                BRepBuilderAPI_MakeVertex vertex(gp_Pnt(xyz[0], xyz[1], xyz[2]));
                toolShape = vertex.Shape();
            }
            else if (type < static_cast<int>(Interface::FITKGeoEnum::FITKDatumType::FDTPlane)) {
                auto refLine = dynamic_cast<Interface::FITKAbsGeoDatumLine*>(datum);
                if (refLine == nullptr) return false;
                double start[3]{}, end[3]{};
                refLine->getPosition(start);
                refLine->getPosition2(end);
                BRepBuilderAPI_MakeEdge edge(gp_Pnt(start[0], start[1], start[2]), gp_Pnt(end[0], end[1], end[2]));
                toolShape = edge.Shape();
            }
            else {
                auto refPlane = dynamic_cast<Interface::FITKAbsGeoDatumPlane*>(datum);
                if (refPlane == nullptr) return false;
                double loc[3]{}, normal[3]{}, up[3]{};
                refPlane->getPlane(loc, normal, up);
                gp_Pln plane(
                    gp_Pnt(loc[0], loc[1], loc[2]),
                    gp_Dir(normal[0], normal[1], normal[2])
                );
                BRepBuilderAPI_MakeFace face(plane);
                toolShape = face.Shape();
            }
            if (toolShape.IsNull()) return false;
            splitter.AddTool(toolShape);
        }
        else {
            auto toolCmd = geoCmdList->getDataByID(m_ToolShape.cmdId());
            if (toolCmd == nullptr) return false;
            auto toolShape = toolCmd->getShapeT<FITKOCCTopoShape>(m_ToolShape.virtualTopoId());
            if (toolShape == nullptr) return false;
            TopoDS_Shape toolTopoShape = toolShape->getTopoShape();
            if (toolTopoShape.IsNull()) return false;
            splitter.AddTool(toolTopoShape);
        }

        // 更新分割
        try {
            splitter.Perform();

            BRep_Builder aBuilder;
            TopoDS_Compound aCompound;
            aBuilder.MakeCompound(aCompound);

            TopExp_Explorer exp;
            int index = 0;
            for (exp.Init(splitter.Shape(), TopAbs_SOLID); exp.More(); exp.Next())
            {
                aBuilder.Add(aCompound, exp.Current());
            }
            _occShapeAgent->updateShape(aCompound);
        }
        catch (...)
        {
            printLog(tr("Failed to split!"), 3);
            return false;
        }
        return true;
    }
}


